#ifndef TIME_PROVIDERS_HPP
#define TIME_PROVIDERS_HPP

#include "random.h"
#include <cmath>
#include <vector>
#include <sstream>
#include <fstream>
using namespace std;

class TP {
public:
  TP(rng01_t &rng, const string label)
    : rng(rng)
#ifdef LOG
    , logfile(*new ofstream((label + "_dist.dat").c_str()))
#endif  
  {}

  ~TP() {
#ifdef LOG
    delete &logfile;
#endif
  }

  virtual double get_time(unsigned long = 1) = 0;

protected:
  rng01_t &rng;
#ifdef LOG
  ofstream &logfile;
#endif
};

class TP_NegExp : public TP {
public:
  TP_NegExp(vector<double> &parameters, rng01_t &rng, string ID)
    : TP(rng, ID), rate(parameters[0]) {}
    
  virtual double get_time(unsigned long load) {
    double res = -log(double(1) - rng()) / rate;
#ifdef LOG
    logfile << res << endl;
#endif
    return res;
  }

private:
  double rate;
};

class TP_Erlang : public TP {
public:
  TP_Erlang(vector<double> &parameters, rng01_t &rng, string ID)
    : TP(rng, ID), k(parameters[0]), rate(parameters[1])
  {
    rate *= k;
  }

  virtual double get_time(unsigned long load) {
    double res = 1;
    for(unsigned int i=0; i<k; ++i)
      res *= (double(1) - rng());
    res = -(log(res)) / (rate);
#ifdef LOG
    logfile << res << endl;
#endif
    return res;
  }

private:
  unsigned int k;
  double rate;
};

class TP_Custom01 : public TP {
public:
  TP_Custom01(vector<double> &parameters, rng01_t &rng, string ID)
    : TP(rng, ID),
      min1(parameters[0]), max1(parameters[1]),
      min2(parameters[2]), max2(parameters[3]),
      mean(parameters[4]), sd(sqrt(parameters[5])),
      min3(parameters[6]), max3(parameters[7]) {}

  virtual double get_time(unsigned long load) {
    double res = 0;
    double t1 = min1 + (max1 - min1)*rng(); //uniform[min1, max1]
    double t2 = min2 + (max2 - min2)*rng(); //uniform[min2, max2]
    //normal(mean, sd^2) trunked to [min_whole, max_whole]
    double t3 = 0;
    while(true) {
      t3 = 0;
      for(unsigned int i=0; i<48; ++i)
	t3 += rng();
      t3 = (t3 - 24) * 2 * sd + mean;
      //check the range
      if(t3 >= min3 && res <= max3)
	break;
    }
    return t1 + t2 + t3;
  }

private:
  double min1, max1, min2, max2;
  double mean, sd;
  double min3, max3;
};

#endif
